package comet.util;

import java.util.ArrayList;
import java.util.Iterator;


import com.hp.hpl.jena.ontology.AllValuesFromRestriction;
import com.hp.hpl.jena.ontology.CardinalityRestriction;
import com.hp.hpl.jena.ontology.ComplementClass;
import com.hp.hpl.jena.ontology.ConversionException;
import com.hp.hpl.jena.ontology.EnumeratedClass;
import com.hp.hpl.jena.ontology.HasValueRestriction;
import com.hp.hpl.jena.ontology.IntersectionClass;
import com.hp.hpl.jena.ontology.MaxCardinalityRestriction;
import com.hp.hpl.jena.ontology.MinCardinalityRestriction;
import com.hp.hpl.jena.ontology.ObjectProperty;
import com.hp.hpl.jena.ontology.OntClass;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.ontology.OntModelSpec;
import com.hp.hpl.jena.ontology.OntProperty;
import com.hp.hpl.jena.ontology.OntResource;
import com.hp.hpl.jena.ontology.Restriction;
import com.hp.hpl.jena.ontology.SomeValuesFromRestriction;
import com.hp.hpl.jena.ontology.UnionClass;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.util.iterator.ExtendedIterator;

/**
 * This class permit users to manipulate an ontology.
 * It contains severals functions just about ontology.
 * @author hacene_m
 *
 */

public class OntologyHelper {
	/**
	 * This method load an ontology from a file 
	 * @param docURI URI of the ontology which will be load as an OntModel
	 * @param locationURL adresse where a copy is available
	 * @return OntModel representing the ontology imported
	 */
	public static  OntModel loadOntology(String docURI, String locationURL) {
		OntModel onto;
		onto = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM,null);
		onto.getDocumentManager().addAltEntry(docURI,locationURL);
		// read the source document
		onto.read(docURI);		
		//FileManager.get().loadModel(source);
		setClassesLabels(onto);
		
		return onto;
	}
	
	/**
	 * This method give an anonymous class a label
	 * @param oc anonymous class
	 */
	public static void grantClassNameForAnonymousClass(OntClass oc) {
		if (oc.isEnumeratedClass()) {
			EnumeratedClass e = oc.asEnumeratedClass();
			String label = "EnumClass";
			try {
				ExtendedIterator listOneOf = e.listOneOf();
				while (listOneOf.hasNext()) {
					OntResource oneOf = (OntResource) listOneOf.next();
					label = label + oneOf.getLocalName() + "__";
				} 
			}
			catch (ConversionException ce) {
				label = label + "[" + oc.toString() + "]";
			}
			oc.setLabel(label, null);
		}
		if (oc.isComplementClass()) {
			ComplementClass c = oc.asComplementClass();
			String label = "";
			if (c.getOperand().getLocalName() == null) {
				OntClass complementClass = c.getOperand();
				grantClassNameForAnonymousClass(complementClass);
				label = complementClass.getLabel(null);
			}
			else
				label = c.getOperand().getLocalName();
			oc.setLabel("ComplementOf" + label, null);
		}
		if (oc.isIntersectionClass()) {
			IntersectionClass i = oc.asIntersectionClass();
			String label = "IntersectionOf";
			ExtendedIterator classesInIntersection = i.listOperands();
			while (classesInIntersection.hasNext()) {
				OntClass c = (OntClass) classesInIntersection.next();
				String classeInIntersectionLabel = "";
				if (c.getLocalName() == null) {
					grantClassNameForAnonymousClass(c);
					classeInIntersectionLabel = c.getLabel(null);
				}	
				else
					classeInIntersectionLabel = c.getLocalName();
				label = label + classeInIntersectionLabel + "__";
			}
			oc.setLabel(label, null);
		}
		if (oc.isUnionClass()) {
			UnionClass u = oc.asUnionClass();
			String label = "UnionOf";
			ExtendedIterator classesInUnion = u.listOperands();
			while (classesInUnion.hasNext()) {
				OntClass c = (OntClass) classesInUnion.next();
				if (c.getLocalName() == null)
					grantClassNameForAnonymousClass(c);
				else 
					c.setLabel(c.getLocalName(), null);
				label = label + c.getLabel(null) + "__";
			}
			oc.setLabel(label, null);
		}
		if (oc.isRestriction()) {
			Restriction r = oc.asRestriction();
			if (r.isCardinalityRestriction()) {
				CardinalityRestriction cr = r.asCardinalityRestriction();
				oc.setLabel("Cardinlity" + cr.getCardinality() + "On" + cr.getOnProperty().getLocalName(), null);
			}
			if (r.isMaxCardinalityRestriction()) {
				MaxCardinalityRestriction maxcr = r.asMaxCardinalityRestriction();
				oc.setLabel("MaxCardinality" + maxcr.getMaxCardinality() + "On" + maxcr.getOnProperty().getLocalName(), null);
			}
			if (r.isMinCardinalityRestriction()) {
				MinCardinalityRestriction mincr = r.asMinCardinalityRestriction();
				oc.setLabel("MinCardinality" + mincr.getMinCardinality() + "On" + mincr.getOnProperty().getLocalName(), null);
			}
			if (r.isHasValueRestriction()) {
				HasValueRestriction hvr = r.asHasValueRestriction();
				oc.setLabel("HasValues" + hvr.getHasValue() + "On" + hvr.getOnProperty().getLocalName(), null);
			}
			if (r.isSomeValuesFromRestriction()) {
				String classFromLabel = "";
				SomeValuesFromRestriction svr = r.asSomeValuesFromRestriction();
				if (svr.getSomeValuesFrom().getLocalName() == null) {
					OntClass c = (OntClass) svr.getSomeValuesFrom();
					grantClassNameForAnonymousClass(c);
					classFromLabel = c.getLabel(null);
				}
				else 
					classFromLabel = svr.getSomeValuesFrom().getLocalName();
				oc.setLabel("SomeValuesFrom" + classFromLabel + "On" + svr.getOnProperty().getLocalName(), null);
			}
			if (r.isAllValuesFromRestriction()) {
				String classFromLabel = "";
				AllValuesFromRestriction avr = r.asAllValuesFromRestriction();
				if (avr.getAllValuesFrom().getLocalName() == null) {
					OntClass c = (OntClass) avr.getAllValuesFrom();
					grantClassNameForAnonymousClass(c);
					classFromLabel = c.getLabel(null);
				}
				else
					classFromLabel = avr.getAllValuesFrom().getLocalName();
				oc.setLabel("AllValuesFrom" + classFromLabel + "On" + avr.getOnProperty().getLocalName(), null);
			}
		}
	}
	
	
	
	
	/**
	 * This method get ontology concepts' labels
	 * @param onto ontology the algorithm get concepts' labels
	 * @return List of ontology concepts' labels
	 */
	public static ArrayList<String> getLabelsOfConcepts(OntModel onto) {
		ArrayList<String> labels = new ArrayList<String>();
		int j=0;
		for (Iterator<OntClass> i = onto.listClasses();  i.hasNext(); ) {
          OntClass oc = (OntClass) i.next();
          labels.add(j, oc.getLabel(null));
          j++;
		}
		return labels;
	}
	
	/**
	 * This method get ontology properties' labels
	 * @param onto ontology the algorithm get properties' labels
	 * @return List of ontology properties' labels
	 */
	public static ArrayList<String> getLabelsOfObjectProperties(OntModel onto) {
		ArrayList<String> labels = new ArrayList<String>();
		int j=0;
		Iterator<OntProperty> i = onto.listAllOntProperties();
		while (i.hasNext()) {
			OntProperty p = (OntProperty) i.next();
	        labels.add(j, p.getLocalName());
	        j++;
		}
		return labels;
	}
	
	/**
	 * Gives a label to each ontology's concept
	 * @param onto ontology in which labels are added
	 */
	public static void setClassesLabels(OntModel onto) {
		for (Iterator<OntClass> i = onto.listClasses();  i.hasNext(); ) {
			OntClass oc = (OntClass) i.next();

			//Anonymous Class
	        if(oc.getLocalName()==null)
	          grantClassNameForAnonymousClass(oc);
	        else
	          oc.setLabel(oc.getLocalName(), null);	
		}
	}
	
	/**
	 * Get incoming properties of a concept.
	 * @param ontology ontology in which algorithm get properties
	 * @param oc concept in which algorithm get incoming properties
	 * @return List of incoming properties for the concept given in parameters
	 */
	public static ArrayList<OntProperty> getIncomingProperties(OntModel ontology, OntClass oc) {
		Iterator<OntProperty> properties = ontology.listAllOntProperties();
		ArrayList<OntProperty> inProp = new ArrayList<OntProperty>();
		while (properties.hasNext()) {
			OntProperty p = properties.next();
			if (p.getDomain() == null || p.getRange() == null) continue;
			if (p.getEquivalentProperty() != null) continue;
			if (p.getRange().equals(oc))
				inProp.add(p);
		}
		return inProp;
	}
	
	/**
	 * This method gets object properties for which class given in parameter is the range.
	 * @param ontology ontology in which we look for properties
	 * @param oc class supposed to be the range
	 * @return List of incoming object properties
	 */
	public static ArrayList<OntProperty> getIncomingObjectProperties(OntModel ontology, OntClass oc) {
		ExtendedIterator properties = ontology.listObjectProperties();
		ArrayList<OntProperty> inProp = new ArrayList<OntProperty>();
		while (properties.hasNext()) {
			ObjectProperty p = (ObjectProperty) properties.next();
			if (p.getDomain() == null || p.getRange() == null) continue;
			if (p.getEquivalentProperty() != null) continue;
			if (p.getRange().equals(oc))
				inProp.add(p);
		}
		return inProp;
	}
	
	/**
	 * Get properties which describe the concept given in parameters.
	 * <p>That means, algorithm get properties in which the domain is the concept given in parameters.
	 * @param oc concept for which outgoing properties are search 
	 * @return List of outgoing properties for the concept given in parameters
	 */
	public static ArrayList<OntProperty> getOutgoingProperties(OntClass oc) {
		Iterator<OntProperty> propIt  = oc.listDeclaredProperties();
        ArrayList<OntProperty> outProp = new ArrayList<OntProperty>();
        while (propIt.hasNext()) {
                OntProperty p = propIt.next();
                if (p.getDomain() == null) continue;
                if (p.getEquivalentProperty() != null) continue;
                if (p.getDomain().equals(oc))
                	outProp.add(p); 
        }
        return outProp;
	}
	
	/**
	 * This method gets object properties that class given in parameter is the domain.
	 * @param oc class described by properties
	 * @return List of outgoing object properties
	 */
	public static ArrayList<OntProperty> getOutgoingObjectProperties(OntClass oc) {
		Iterator<OntProperty> propIt  = oc.listDeclaredProperties();
        ArrayList<OntProperty> outProp = new ArrayList<OntProperty>();
        while (propIt.hasNext()) {
                OntProperty p = propIt.next();
                if(!(p instanceof ObjectProperty)) continue;
                if (p.getDomain() == null) continue;
                if (p.getEquivalentProperty() != null) continue;
                if (p.getDomain().equals(oc))
                	outProp.add(p); 
        }
        return outProp;
	}
	
	/**
	 * Method which get siblings of a concept
	 * @param ontology ontology containing the concept studied
	 * @param oc concept for which algorith searches siblings
	 * @return List of concept sibling to the concept given in parameter
	 */
	public static ArrayList<OntClass> getSiblings(OntModel ontology, OntClass oc) {
		Iterator<OntClass> superClasses = oc.listSuperClasses();
		ArrayList<OntClass> siblings = new ArrayList<OntClass>();
		while (superClasses.hasNext()) {
			OntClass mother = superClasses.next();
			Iterator<OntClass> subClasses = mother.listSubClasses();
			while (subClasses.hasNext()) {
				OntClass sister = subClasses.next();
				if (sister.equals(oc)) continue;
				siblings.add(sister);
			}
		}
		return siblings;
	}	
	
	/**
	 * Method that get label for the class given in parameter
	 * @param oc class that we get label
	 * @return label if exists, empty string either
	 */
	public static String getClassLabel(OntClass oc) {
		if(oc.getLocalName()!=null)
			return oc.getLocalName();
		else return "";
		//return oc.getLabel(null);
	}
	
	/**
	 * This method get root classes, that mean classes that don't have super-class.
	 * @param ontology ontology in wich we look for root classes
	 * @return list of root classes
	 */
	public static ArrayList<OntClass> getRoots(OntModel ontology) {
		ArrayList<OntClass> roots = new ArrayList<OntClass>();
		if (Settings.THING) {
			roots.add(ontology.getOntClass("http://www.w3.org/2002/07/owl#Thing"));
		}
		else {
			ExtendedIterator namedClasses = ontology.listNamedClasses();
			while(namedClasses.hasNext()) {
				OntClass classe = (OntClass) namedClasses.next();
				if(!classe.hasSuperClass()) {
					roots.add(classe);
				}
				else {
					if (classe.hasSuperClass(ontology.getResource("http://www.w3.org/2002/07/owl#Thing"))) {
						roots.add(classe);
					}
				}
			}
		}
		
		return roots;
	}

	public static OntClass geConcept(OntModel onto, String label) {
		for (Iterator<OntClass> i = onto.listClasses();  i.hasNext(); ) {
	          OntClass oc = (OntClass) i.next();
	          if(oc.getLabel(null)==label) return oc;
			}
		return null;
	}
}

